// 执行命令  ts-node Generic.ts
// 泛型
const identity = arg => arg
type idBoolean = (arg: boolean) => boolean;
type idNumber = (arg: number) => number;
// T 是一个抽象类型，只有在调用的时候才确定它的值
function identity1<T>(arg: T): T {
  return arg;
}
identity1('hello')
identity1(2)
identity1(true)
function identity2 <T, U>(value: T, message: U) : T {
  console.log(message);
  return value;
}
identity2<Number, string>(68, "Semlinker")
identity2(68, "Semlinker")


// 泛型约束
function trace<T>(arg: T): T {
  // console.log(arg.size); Error
  return arg;
}
interface Sizeable {
  size: number;
}
function trace1<T extends Sizeable>(arg: T): T {
  console.log(arg.size)
  return arg
}


// 泛型工具类型
// typeof  的主要用途是在类型上下文中获取变量或者属性的类型
interface Person4 {
  name: string;
  age: number;
}
const sem1: Person4 = { name: "sem", age: 99 }
type Sem = typeof sem1;
const sem2: Sem = { name: '6366', age: 22 }
// 可以对嵌套对象执行相同的操作
const Message = {
  name: "jimmy",
  age: 18,
  address: {
    province: '四川',
    city: '成都'   
  }
}
type message = typeof Message;
const Message1: message = {
  name: "222",
  age: 33,
  address: {
    province: '四川2',
    city: '成都2'
  }
}
// typeof 操作符除了可以获取对象的结构类型之外，它也可以用来获取函数对象的类型
function toArray(x: number): Array<number> {
  return [x]
}
type Func = typeof toArray;


// keyof  keyof 操作符是在 TypeScript 2.1 版本引入的，该操作符可以用于获取某种类型的所有键，其返回类型是联合类型
interface Person {
  name: string;
  age: number;
}

type K1 = keyof Person; // "name" | "age"
type K2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join" 
type K3 = keyof { [x: string]: Person };  // string | number

const k11: K1 = 'age'
const k21: K2 = 'length'
const k31: K3 = 1
// 字符串索引  ???存疑
interface StringArray {
  // 字符串索引 -> keyof StringArray => string | number
  [index: string]: string; 
}
// 数字索引
interface StringArray1 {
  // 数字索引 -> keyof StringArray1 => number
  [index: number]: string;
}
const StringArray11: StringArray1 = ['23']

function prop<T extends object, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}
type Todo = {
  id: number;
  text: string;
  done: boolean;
}
const todo: Todo = {
  id: 1,
  text: "Learn TypeScript keyof",
  done: false
}
const id11 = prop(todo, "id"); // const id: number


// in
type Keys = "a" | "b" | "c"
type Obj = {
  [p in Keys]: any
} // -> { a: any, b: any, c: any }
const OBj1: Obj = {
  a: 1,
  b: true,
  c: null
}


// infer ???存疑 在条件类型语句中，可以用 infer 声明一个类型变量并且对它进行使用。
type ReturnType111<T> = T extends (
  ...args: any[]
 ) => infer R ? R : any
//  const ReturnType11111: ReturnType111<number> = { a: 1 }


// extends   有时候我们定义的泛型不想过于灵活或者说想继承某些类等，可以通过 extends 关键字添加泛型约束。
interface Lengthwise {
  length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);
  return arg;
}
loggingIdentity({ length: 9 })


// 索引类型
let person = {
  name: 'name',
  age: 23
}
function getValues(person: any, keys: string[]) {
  return keys.map(key => person[key])
}
console.log(getValues(person, ['age2']))
function getValues1<T, K extends keyof T>(person: T, keys: K[]): T[K][] {
  return keys.map(key => person[key]);
}
console.log(getValues1(person, ['age']))

class Person21 {
  name: string;
  age: number;
}
type MyType = Person['name']
const mytype: MyType = '1'


// 映射类型
interface TestInter {
  name: string;
  age: number;
}
type OptionalTestInterface<T> = {
  [p in keyof T]+?: T[p]
}
type newTestInterface = OptionalTestInterface<TestInter>


// Partial   将类型的属性变成可选
interface UserInfo {
  id: string
  name: string
  fruits: {
    appleNumber: number;
    orangeNumber: number;
  }
}
type NewUserInfo = Partial<UserInfo>
const NewUserInfo1: NewUserInfo = { id: '1' }
// Partial<T> 有个局限性，就是只支持处理第一层的属性
const NewUserInfo2: NewUserInfo = {
  id: '1',
  fruits: {
    appleNumber: 1,
    orangeNumber: 23
  }
}


// DeepPartial
type DeepPartial<T> = {
  [U in keyof T]?: T[U] extends object ? DeepPartial<T[U]> : T[U]
}
type PartialedWindow = DeepPartial<UserInfo>; // 现在T上所有属性都变成了可选啦
const DeepPartial1: PartialedWindow = {
  id: '1',
  fruits: {
    appleNumber: 1,
  }
}


// Required
type Required1<T> = {
  [P in keyof T]-?: T[P]
}
const Required11: Required1<UserInfo> = {
  id: '1',
  name: '2',
  fruits: {
    appleNumber: 1,
    orangeNumber: 2
  }
}


// Readonly
type Readonly1<T> = {
  readonly [P in keyof T]: T[P];
}
let Readonly11: Readonly1<UserInfo> = {
  id: '1',
  name: '2',
  fruits: {
    appleNumber: 1,
    orangeNumber: 2
  }
}
// Readonly11.id = '2' // Error


// Pick  Pick 从某个类型中挑出一些属性出来
type Pick1<T, K extends keyof T> = {
  [P in K]: T[P]
}
type Pick11 = Pick<UserInfo, "id">
const pick: Pick11 = {
  id: "3"
}


// Record  Record<K extends keyof any, T> 的作用是将 K 中所有的属性的值转化为 T 类型。
interface PageInfo {
  title: string
}
type Page = "home" | "about" | "contact"
type Record1<K extends keyof any, T> = {
  [P in K]: T
}
const Record11: Record1<Page, PageInfo> = {
  home: { title: '1' },
  about: { title: '2' },
  contact: { title: '3' }
}


// ReturnType  ???存疑  用来得到一个函数的返回值类型
type ReturnTypeR<T extends (...args: any[]) => any> = T extends (
  ...args: any[]
) => infer R ? R : any
type Func1 = (value: number) => string
const foo1: ReturnTypeR<Func1> = "1"


// Exclude  Exclude<T, U> 的作用是将某个类型中属于另一个的类型移除掉。
type Exclude1<T, U> = T extends U ? never : T
type T0 = Exclude1<'a' | 'b' | 'c', 'a'>
const T00: T0 = 'c'


// Extract  Extract<T, U> 的作用是从 T 中提取出 U。
type Extract1<T, U> = T extends U ? T : never
type T1 = Extract1<string | number | (() => void), Function>
const T11: T1 = () => {}


// Omit   Omit<T, K extends keyof any> 的作用是使用 T 类型中除了 K 类型的所有属性，来构造一个新的类型。
type Omit1<T, K extends keyof any> = Pick1<T, Exclude1<keyof T, K>>
interface Todo1 {
  title: string;
  description: string;
  completed: boolean;
}
type TodoPreview = Omit1<Todo1, "description">;
const todo1: TodoPreview = {
  title: "Clean room",
  completed: false,
};


// NonNullable  NonNullable<T> 的作用是用来过滤类型中的 null 及 undefined 类型。
type NonNullable1<T> = T extends (null | undefined) ? never : T
type NonNullable11 = NonNullable1<string | number | undefined>
const NonNullable111: NonNullable11 = undefined  // ???存疑


// Parameters    Parameters<T> 的作用是用于获得函数的参数类型组成的元组类型。
type Parameters1<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;



// console.log('Generic: ', id11)